/**
 * A class for performing common musical calculations.
 * Candidate number: 35834
 */
package tv.starfu.utils;

import java.util.TreeMap;

public final class Music {
    private static final String[] PITCH = {"Ab", "A", "Bb", "B", "C", "Db", "D", "Eb", "E", "F", "Gb", "G"};
    private static final int NUM_OCTAVES = 8;
    private static final float NOTES_IN_OCTAVE = 12f;
    private static final int NUM_TONES = (int) (NUM_OCTAVES * NOTES_IN_OCTAVE);
    private static final Note[] NOTES = new Note[NUM_TONES];
    
    private static final float A4 = 432.0f;
    private static final float A0 = (float) ((A4 * Math.pow(2.0, 1.0 / NOTES_IN_OCTAVE)) / 16);
    
    private static TreeMap<Integer, Float> NumToFreq;
    private static TreeMap<Integer, String> NumToName;
    
    private static TreeMap<Float, Integer> FreqToNum;
    private static TreeMap<Float, String> FreqToName;
    
    private static TreeMap<String, Integer> NameToNum;
    private static TreeMap<String, Float> NameToFreq;
    
    public static final int[] major = { 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1 };
    public static final int[] minor = { 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0 };
    public static final int[] hMinor= { 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0 };
    
    public static int bpm = 120;
    public final static int tempo = bpm_to_ms(bpm);
    /*
     * creates and fills note-tone-frequency maps
     */
    public Music() {
        NumToFreq   = new TreeMap();
        NumToName   = new TreeMap();
        FreqToNum   = new TreeMap();
        FreqToName  = new TreeMap();
        NameToNum   = new TreeMap();
        NameToFreq  = new TreeMap(); 
           
        fillNotes();
        createMaps(); 
    }
    
    
    /* Common scales */
    public static int[] majorScale(int root) {
        int[] scale = new int[7];
        return scale;
    }
    
    public static float[] majorScale(float root) {
        float[] scale = new float[7];
        return scale;
    }
    
    
    public static int[] minorScale(int root) {
        int[] scale = new int[7];
        return scale;
    }
    
    public static float[] minorScale(float root) {
        float[] scale = new float[7];
        return scale;
    }
    
    
    public static int[] hMinorScale(int root) {
        int[] scale = new int[7];
        return scale;
    }
    
    public static float[] hMinorScale(float root) {
        float[] scale = new float[7];
        return scale;
    }
    
    
    public static int[] majorPentatonic(int root) {
        int[] scale = new int[7];
        return scale;
    }
    
    public static float[] majorPentatonic(float root) {
        float[] scale = new float[7];
        return scale;
    }
    
    public static int[] minorPentatonic(int root) {
        int[] scale = new int[7];
        return scale;
    }
    
    public static float[] minorPentatonic(float root) {
        float[] scale = new float[7];
        return scale;
    }
    
    
    
    public static int[] majorTriad(int root) {
        int[] scale = new int[3];
        return scale;
    }
    
    public static float[] majorTriad(float root) {
        float[] scale = new float[3];
        return scale;
    }
    
    
    public static int[] minorTriad(int root) {
        int[] scale = new int[3];
        return scale;
    }
    
    public static float[] minorTriad(float root) {
        float[] scale = new float[7];
        return scale;
    }
    
//        public static int[] chordFunction(int root, boolean major, ) {
//        int[] scale = new int[7];
//        return scale;
//    }
   
    
    
    
    
    /**
     * Get the frequency in Hz from a note number where 0 = A0.
     * @param  number   note number
     * @return float    frequency in Hz
     */
    public static float freq(int number)
        { return NumToFreq.get(number) ; }

    /**
     * Get the frequency in Hz from a note name eg "A4".
     * @param  name   note name
     * @return float  frequency in Hz
     */    
    public static float freq(String name) 
        { return NameToFreq.get(name) ; }
    
    /**
     * Get the integer number of a note from its name eg "A4".
     * @param  name   note name
     * @return int    note number
     */    
    public static int num(String name)
        { return NameToNum.get(name) ; }
    
    /**
     * Find the closest note number from a given frequency.
     * @param  freq   frequency
     * @return int    note number
     */        
    public static int num(float freq) {
        float key;
        int value;
        if (FreqToNum.get(freq) == null) {
            key = FreqToNum.ceilingKey(freq);
            value = FreqToNum.get(key);
        } else {
            value = FreqToNum.get(freq);
        }
        return value;
    }
    
    /**
     * Find note name from the given number.
     * @param  number   note number
     * @return string   note name
     */
    public static String name(int number)
        { return NumToName.get(number) ; }
    
     /**
     * Find the closest note name from a given frequency.
     * @param  name     note name
     * @return float    frequency
     */
    public static String name(float freq) {
        float key;
        String value;
        if (FreqToName.get(freq) == null) {
            key = FreqToName.ceilingKey(freq);
            value = FreqToName.get(key);
        } else {
            value = FreqToName.get(freq);
        }
        return value;
    }
  
    
    private static void fillNotes() {
        /* Create an empty array of Strings */
        String[] pitch_octave = new String[NUM_TONES];

        for (int i = 0; i < NUM_TONES; ++i) {
            int num = i + 1;
            int tone = num % (int) NOTES_IN_OCTAVE;
            int oct = (int) (num / NOTES_IN_OCTAVE);
            String noteName = PITCH[tone] + oct;
            pitch_octave[i] = noteName;

            float tempFreq = (float) ((A4 * Math.pow(2.0f, (num-1) / NOTES_IN_OCTAVE)) / 16);
            NOTES[i] = new Note(i, pitch_octave[i], tempFreq);
            System.out.println(NOTES[i].getName());
            System.out.println(NOTES[i].getNumber());
            System.out.println(NOTES[i].getFrequency());
        }

    }

    public void createMaps() {

        String tempName;
        float tempFreq;
        int tempNum;

        for (int i = 0; i < NUM_TONES; ++i) {
            tempNum = NOTES[i].getNumber();
            tempName = NOTES[i].getName();
            tempFreq = NOTES[i].getFrequency();

            NumToFreq.put(tempNum, tempFreq);
            NumToName.put(tempNum, tempName);

            FreqToName.put(tempFreq, tempName);
            FreqToNum.put(tempFreq, tempNum);

            NameToFreq.put(tempName, tempFreq);
            NameToNum.put(tempName, tempNum);

            
        }

    }
    
    
    // Useful
    public final static int bpm_to_ms(int bpm) { // Convert BPM to ms
            float mins_per_beat = 1f / bpm; // How many minutes per beat?
            float sec_per_beat = mins_per_beat * 60; // Seconds per beat
            float ms_per_beat = sec_per_beat * 1000; // Milliseconds per beat

            return (int) ms_per_beat / 2;
    }
    
       
    
}
